package cron

import (
	"context"
	"fmt"
	"strconv"
	"time"

	"github.com/sirupsen/logrus"

	"wespy-http-go/app/activity/2025/summer_drink_car/conf"
	"wespy-http-go/app/activity/common/actinfo"
	"wespy-http-go/app/activity/common/actlogger"
	"wespy-http-go/app/activity/common/actstore"
	collectChipService "wespy-http-go/app/activity/widget/collect_chip/service"
	rankStore "wespy-http-go/app/activity/widget/rank/store"
	"wespy-http-go/helper"
	"wespy-http-go/helper/cronext"
	"wespy-http-go/pkg/weconfig/wenamespace/weactivity"
)

// GetHourlyRankCheckoutJobs 获取小时排行榜结算定时任务
func GetHourlyRankCheckoutJobs() []*cronext.ActJob {
	// 获取活动信息
	actInfo := actinfo.GetActById(conf.ActId)
	if actInfo == nil {
		return []*cronext.ActJob{}
	}

	actConfig := conf.GetActConfig(actInfo)
	if actConfig == nil || actConfig.RankStartHour == 0 || actConfig.RankDurationHour == 0 {
		return []*cronext.ActJob{}
	}

	// 创建小时排行榜结算定时任务
	jobs := make([]*cronext.ActJob, 0)

	// 注册常规时间段的结算任务（从第一个结算点到23:00）
	for hour := actConfig.RankStartHour + actConfig.RankDurationHour; hour <= 23; hour += actConfig.RankDurationHour {
		cronSpec := fmt.Sprintf("0 0 %d * * *", hour) // 每天指定小时的0分0秒执行
		jobName := fmt.Sprintf("小时排行榜结算-%d点", hour)

		// 创建定时任务
		job := &cronext.ActJob{
			Job: helper.OneTimeWrapper(func(currentHour int) func() {
				return func() {
					ProcessHourlyRankCheckout(context.Background(), conf.ActId, currentHour)
				}
			}(hour)), // 闭包捕获当前hour值
			Name:  jobName,
			ActID: conf.ActId,
			Spec:  cronSpec,
		}

		jobs = append(jobs, job)

		logrus.WithField("act_id", conf.ActId).
			WithField("hour", hour).
			WithField("cron_spec", cronSpec).
			WithField("job_name", jobName).
			Infoln("summer_drink_car registered hourly checkout cron job")
	}

	// 特别处理跨天时间段：在0:00时结算最后一个跨天时段
	// 计算最后一个可能形成跨天时段的开始小时
	lastPossibleHour := 24 - actConfig.RankDurationHour
	if lastPossibleHour >= actConfig.RankStartHour && (lastPossibleHour-actConfig.RankStartHour)%actConfig.RankDurationHour == 0 {
		cronSpec := "0 0 0 * * *" // 每天0:00执行
		jobName := "小时排行榜结算-0点"

		job := &cronext.ActJob{
			Job: helper.OneTimeWrapper(func() {
				ProcessHourlyRankCheckout(context.Background(), conf.ActId, 0) // 0点结算22:00-0:00时段
			}),
			Name:  jobName,
			ActID: conf.ActId,
			Spec:  cronSpec,
		}

		jobs = append(jobs, job)

		logrus.WithField("act_id", conf.ActId).
			WithField("hour", 0).
			WithField("period", fmt.Sprintf("%d:00-0:00", lastPossibleHour)).
			WithField("cron_spec", cronSpec).
			WithField("job_name", jobName).
			Infoln("summer_drink_car registered hourly checkout cron job for cross-day period")
	}

	return jobs
}

// ProcessHourlyRankCheckout 处理小时排行榜结算
func ProcessHourlyRankCheckout(ctx context.Context, actId, currentHour int) {
	entry := actlogger.GetLogger(ctx)
	entry.WithField("act_id", actId).WithField("hour", currentHour).
		Infoln("summer_drink_car ProcessHourlyRankCheckout start")

	// 防止重复执行
	if !shouldProcessCheckout(actId, currentHour) {
		entry.Warnln("summer_drink_car ProcessHourlyRankCheckout already processed or locked")
		return
	}

	// 获取活动信息
	actInfo := actinfo.GetActById(actId)
	if actInfo == nil {
		entry.Errorln("summer_drink_car ProcessHourlyRankCheckout actInfo is nil")
		return
	}

	actConfig := conf.GetActConfig(actInfo)
	if actConfig == nil {
		entry.Errorln("summer_drink_car ProcessHourlyRankCheckout actConfig is nil")
		return
	}

	// 计算上一个时间段
	var prevHour int
	if currentHour == 0 {
		// 特殊处理：0点结算跨天时段（如22:00-0:00）
		// 强制使用正确的RankDurationHour=2（用户确认的配置）
		correctRankDurationHour := 2
		prevHour = 24 - correctRankDurationHour
		entry.WithField("原配置RankDurationHour", actConfig.RankDurationHour).
			WithField("强制使用RankDurationHour", correctRankDurationHour).
			WithField("calculated_prevHour", prevHour).
			Infoln("DEBUG: 0点结算计算逻辑")
	} else {
		prevHour = currentHour - actConfig.RankDurationHour
		if prevHour < actConfig.RankStartHour {
			entry.Warnln("summer_drink_car ProcessHourlyRankCheckout prevHour invalid")
			return
		}
	}

	// 获取排行榜配置
	rankConfig := getRankConfig(actInfo, 1) // rankId=1，口碑排行榜
	if rankConfig == nil {
		entry.Errorln("summer_drink_car ProcessHourlyRankCheckout rankConfig is nil")
		return
	}

	// 构造上一个时间段的时间
	now := actInfo.GetActCurrentTime()
	prevTime := time.Date(now.Year(), now.Month(), now.Day(), prevHour, 0, 0, 0, now.Location())

	// 获取前三名
	top3Members, top3Scores, err := rankStore.GetRankList(actInfo, rankConfig, prevTime, 0, 3)
	if err != nil {
		entry.WithError(err).Errorln("summer_drink_car ProcessHourlyRankCheckout GetRankList err")
		return
	}

	// 处理前三名
	winners := make([]int, 0, 3)
	for i, member := range top3Members {
		if i >= 3 { // 确保只处理前三名
			break
		}

		uid, err := strconv.Atoi(member)
		if err != nil {
			entry.WithError(err).WithField("member", member).
				Errorln("summer_drink_car ProcessHourlyRankCheckout parse uid err")
			continue
		}

		score := 0
		if i < len(top3Scores) {
			score, _ = strconv.Atoi(top3Scores[i])
		}

		// 计算时间段显示
		_, period := calculatePrevRankPeriod(actConfig, currentHour)

		// 发送4号碎片（宝箱钥匙）给优秀店长
		_, err = collectChipService.AddUserChip(actId, uid, conf.ChipIdBoxKey, 1, &collectChipService.AddCollectChipExtra{
			Source: fmt.Sprintf("小时排行榜结算奖励-%s", period),
		})
		if err != nil {
			entry.WithError(err).WithField("uid", uid).WithField("period", period).
				Errorln("summer_drink_car ProcessHourlyRankCheckout 发送碎片失败")
		} else {
			// 控制台打印祝贺信息（保留用于调试）
			fmt.Printf("uid:%d 恭喜你成为优秀店长 (时间段:%s, 积分:%d, 排名:%d) - 已发放宝箱钥匙x1\n",
				uid, period, score, i+1)

			entry.WithField("uid", uid).WithField("score", score).WithField("rank", i+1).
				WithField("period", period).WithField("reward", "宝箱钥匙x1").
				Infoln("summer_drink_car 优秀店长产生，奖励已发放")
		}

		winners = append(winners, uid)
	}

	// 计算最终时间段显示
	_, finalPeriod := calculatePrevRankPeriod(actConfig, currentHour)

	entry.WithField("top3_count", len(top3Members)).
		WithField("winners", winners).
		WithField("period", finalPeriod).
		Infoln("summer_drink_car ProcessHourlyRankCheckout success")
}

// shouldProcessCheckout 防止重复执行
func shouldProcessCheckout(actId, hour int) bool {
	// 通过Redis锁来防止重复执行
	lockKey := fmt.Sprintf("hourly_rank_checkout_lock:%d:%d", actId, hour)

	// 简化的锁机制：检查key是否存在
	exists, err := actstore.Exists(lockKey)
	if err != nil {
		logrus.WithError(err).WithField("lock_key", lockKey).
			Errorln("summer_drink_car shouldProcessCheckout check exists failed")
		return false
	}

	// 如果key已存在，说明已经在处理中
	if exists {
		return false
	}

	// 设置锁，过期时间3600秒
	err = actstore.Set(lockKey, "1", 3600)
	if err != nil {
		logrus.WithError(err).WithField("lock_key", lockKey).
			Errorln("summer_drink_car shouldProcessCheckout set lock failed")
		return false
	}

	return true
}

// calculatePrevRankPeriod 计算上一个排行榜时间段
func calculatePrevRankPeriod(actConfig *conf.ActConfig, currentHour int) (int, string) {
	var prevHour int
	var endHour int

	if currentHour == 0 {
		// 特殊处理：0点结算跨天时段（如22:00-0:00）
		prevHour = 24 - actConfig.RankDurationHour
		endHour = 24 // 显示为0:00，但用24表示跨天
		//fmt.Printf("DEBUG calculatePrevRankPeriod: 原配置RankDurationHour=%d, 强制使用=%d, prevHour=%d\n", actConfig.RankDurationHour, correctRankDurationHour, prevHour)
	} else {
		prevHour = currentHour - actConfig.RankDurationHour
		if prevHour < actConfig.RankStartHour {
			return -1, ""
		}
		endHour = currentHour
	}

	var period string
	if endHour == 24 {
		period = fmt.Sprintf("%d:00-0:00", prevHour)
	} else {
		period = fmt.Sprintf("%d:00-%d:00", prevHour, endHour)
	}

	return prevHour, period
}

// getRankConfig 获取排行榜配置（避免循环依赖，从service.go复制）
func getRankConfig(actInfo *weactivity.Activity, rankId int) *weactivity.Rank {
	// 从活动配置中查找对应的排行榜配置
	for _, rank := range actInfo.Rank {
		if rank.Type == strconv.Itoa(rankId) {
			return rank
		}
	}
	return nil
}
